From a5e1cb8704755905940c7242f62c5e9d85957d65 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 27 Mar 2012 15:20:23 +0200 Subject: [PATCH] x86/hpet: disable before reboot or kexec Linux up to now is not smart enough to properly clear the HPET when it boots, which is particularly a problem when a kdump attempt from running under Xen is being made. Linux itself added code to work around this to its shutdown paths quite some time ago, so let's do something similar in Xen: Save the configuration register settings during boot, and restore them during shutdown. This should cover the majority of cases where the secondary kernel might not come up because timer interrupts don't work. Signed-off-by: Jan Beulich Acked-by: Keir Fraser --- xen/arch/x86/crash.c | 1 + xen/arch/x86/hpet.c | 38 +++++++++++++++++++++++++++++++++----- xen/arch/x86/smp.c | 2 ++ xen/arch/x86/time.c | 2 +- xen/include/asm-x86/hpet.h | 7 ++++++- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/xen/arch/x86/crash.c b/xen/arch/x86/crash.c index 68174b51fa..0e1afa67dd 100644 --- a/xen/arch/x86/crash.c +++ b/xen/arch/x86/crash.c @@ -94,6 +94,7 @@ static void nmi_shootdown_cpus(void) x2apic_enabled = (current_local_apic_mode() == APIC_MODE_X2APIC); disable_IO_APIC(); + hpet_disable(); } void machine_crash_shutdown(void) diff --git a/xen/arch/x86/hpet.c b/xen/arch/x86/hpet.c index 8ead34be60..b5ef54f90a 100644 --- a/xen/arch/x86/hpet.c +++ b/xen/arch/x86/hpet.c @@ -562,7 +562,7 @@ void hpet_broadcast_resume(void) if ( !hpet_events ) return; - hpet_resume(); + hpet_resume(NULL); cfg = hpet_read32(HPET_CFG); @@ -700,10 +700,13 @@ int hpet_legacy_irq_tick(void) return 1; } +static u32 *hpet_boot_cfg; + u64 __init hpet_setup(void) { static u64 __initdata hpet_rate; u32 hpet_id, hpet_period; + unsigned int last; if ( hpet_rate ) return hpet_rate; @@ -728,7 +731,9 @@ u64 __init hpet_setup(void) return 0; } - hpet_resume(); + last = (hpet_id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT; + hpet_boot_cfg = xmalloc_array(u32, 2 + last); + hpet_resume(hpet_boot_cfg); hpet_rate = 1000000000000000ULL; /* 10^15 */ (void)do_div(hpet_rate, hpet_period); @@ -736,24 +741,29 @@ u64 __init hpet_setup(void) return hpet_rate; } -void hpet_resume(void) +void hpet_resume(u32 *boot_cfg) { static u32 system_reset_latch; u32 hpet_id, cfg; - unsigned int i; + unsigned int i, last; if ( system_reset_latch == system_reset_counter ) return; system_reset_latch = system_reset_counter; cfg = hpet_read32(HPET_CFG); + if ( boot_cfg ) + *boot_cfg = cfg; cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY); hpet_write32(cfg, HPET_CFG); hpet_id = hpet_read32(HPET_ID); - for ( i = 0; i <= ((hpet_id >> 8) & 31); i++ ) + last = (hpet_id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT; + for ( i = 0; i <= last; ++i ) { cfg = hpet_read32(HPET_Tn_CFG(i)); + if ( boot_cfg ) + boot_cfg[i + 1] = cfg; cfg &= ~HPET_TN_ENABLE; hpet_write32(cfg, HPET_Tn_CFG(i)); } @@ -762,3 +772,21 @@ void hpet_resume(void) cfg |= HPET_CFG_ENABLE; hpet_write32(cfg, HPET_CFG); } + +void hpet_disable(void) +{ + unsigned int i; + u32 id; + + if ( !hpet_boot_cfg ) + return; + + hpet_write32(*hpet_boot_cfg & ~HPET_CFG_ENABLE, HPET_CFG); + + id = hpet_read32(HPET_ID); + for ( i = 0; i <= ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); ++i ) + hpet_write32(hpet_boot_cfg[i + 1], HPET_Tn_CFG(i)); + + if ( *hpet_boot_cfg & HPET_CFG_ENABLE ) + hpet_write32(*hpet_boot_cfg, HPET_CFG); +} diff --git a/xen/arch/x86/smp.c b/xen/arch/x86/smp.c index 77daca5ce2..fbab8d0340 100644 --- a/xen/arch/x86/smp.c +++ b/xen/arch/x86/smp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -375,6 +376,7 @@ void smp_send_stop(void) local_irq_disable(); __stop_this_cpu(); disable_IO_APIC(); + hpet_disable(); local_irq_enable(); } diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c index 91682bd0f1..f944e3d8f3 100644 --- a/xen/arch/x86/time.c +++ b/xen/arch/x86/time.c @@ -377,7 +377,7 @@ static int __init init_hpet(struct platform_timesource *pts) static void resume_hpet(struct platform_timesource *pts) { - hpet_resume(); + hpet_resume(NULL); } static struct platform_timesource __initdata plt_hpet = diff --git a/xen/include/asm-x86/hpet.h b/xen/include/asm-x86/hpet.h index fe9f9b6ded..bb2c4b4af8 100644 --- a/xen/include/asm-x86/hpet.h +++ b/xen/include/asm-x86/hpet.h @@ -55,7 +55,12 @@ extern unsigned long hpet_address; * Return value is zero if HPET is unavailable. */ u64 hpet_setup(void); -void hpet_resume(void); +void hpet_resume(u32 *); + +/* + * Disable HPET hardware: restore it to boot time state. + */ +void hpet_disable(void); /* * Callback from legacy timer (PIT channel 0) IRQ handler. -- 2.30.2